/*
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *
 *   版权所有  2014-2015 成都星锐蓝海网络科技有限公司
 *   商业许可请联系  +86-18682011860    QQ:66442834
 *   
 */

#include <stdio.h>
#include <unistd.h>
#include <netinet/in.h>
#include "xyz_log.h"
#include "wmpctrl.h"
#include "udphelp.h"
#include "wtpinfo.h"
#include "wtpnl.h"
#include "wmpuser.h"
#include "util.h"
#include "global.h"


extern int G_wmpsock;
extern struct sockaddr_in G_wmpaddr;

extern int G_nlksock;

static int wmpRunHandleACMsg(int fd);

static int wmmNewUserRep(WMMSG *msg);
static int wmmUserAccrReq(WMMSG *msg);
static int wmmUserLeftRep(WMMSG *msg);
static int wmmUserDropReq(WMMSG *msg);
static int wmmUserAcctRep(WMMSG *msg);
static int wmmRebootReq(WMMSG *msg);

static int wmmEchoReq(void);
static int wmmEchoRep(WMMSG *msg);
static int wmmFirmUpgrReq(WMMSG *msg);

static int S_echonum = 0;

extern int nsap_firm_upgr_thread(void);
extern uint16_t wtpInfoGetSEQNUM(void);


/*
 *
 */
void wmpRun(int *state)
{
	int rc = 0;
	int addrlen = sizeof(struct sockaddr_in);
	fd_set fset;
	struct timeval tv = {0, 0};
	int max_fd = G_wmpsock > G_nlksock ? G_wmpsock : G_nlksock;

	if (S_echonum > 1) {
		tv.tv_sec = 3;
		xyz_log_debug("-RUN- wait Echo-Rep timeout %d times, select timeout 3s, shoud be %ds", S_echonum - 1, wtpInfoGetEcho());
	} else {
		tv.tv_sec = wtpInfoGetEcho();
		xyz_log_debug("-RUN- select timeout %ds", wtpInfoGetEcho());
	}

	do {
		FD_ZERO(&fset);
		FD_SET(G_wmpsock, &fset);
		FD_SET(G_nlksock, &fset);
		rc = select(max_fd + 1, &fset, NULL, NULL, &tv);
		if (rc > 0) {
			if (FD_ISSET(G_wmpsock, &fset)) {
				wmpRunHandleACMsg(G_wmpsock);
			}
			if (FD_ISSET(G_nlksock, &fset)) {
				wtpnlRecv(G_nlksock);
			}
		}
	} while (rc > 0);
	if (S_echonum == WTP_ECHO_MAX) {
		xyz_log_error("This WTP lost contact");
		S_echonum = 0;
		xyz_log_error("Echo ac timeout, jump status to wait");
		*state = WTP_STATE_WAIT;
		return;
	}
	if (0 == rc) {
		rc = wmmEchoReq();
		S_echonum++;
		if (rc) {
			xyz_log_error("Send ECHO-REQ error");
		} else {
			*state = WTP_STATE_RUN;
		}
	} else {
		*state = WTP_STATE_WAIT;
		S_echonum = 0;
	}
}

static int wmpRunHandleACMsg(int fd)
{
	WMMSG msg;
	struct sockaddr_in from;
	int len = 0;

	memset(&msg, 0, sizeof(msg));
	memset(&from, 0, sizeof(from));
	len = udpRecv(fd, msg.buff, WMM_BUFF_SIZE, &from);
	if (len < 1) {
		xyz_log_error("recvfrom : %s", strerror(errno));
		return -1;
	}
	msg.totlen = len;
	if (wmmPrepRecvd(&msg)) {
		xyz_log_error("Preprocess message error");
		return -1;
	}
	xyz_log_debug("AC message code=%d, length=%d", msg.code, len);
	switch (msg.code) {
	case WMM_CODE_ECHO_REP :
		wmmEchoRep(&msg);
		break;
	case WMM_CODE_REBOOT_REQ :
		if (wmmRebootReq(&msg)) {
			xyz_log_error("Handle REBOOT-REQ error");
		} else {
			xyz_log_warn("Req me reboot!");
			sleep(2);
			utilDoCMD("reboot");
		}
		break;
	case WMM_CODE_IMGUP_REQ:
		if (wmmFirmUpgrReq(&msg)) {
			xyz_log_error("Handle Firm-Upgrade-REQ error");
		}
		break;
	case WMM_CODE_NEWUSR_REP :
		if (wmmNewUserRep(&msg)) {
			xyz_log_error("Handle NEW-USER-REP error");
		}
		break;
	case WMM_CODE_USRACCR_REQ :
		if (wmmUserAccrReq(&msg)) {
			xyz_log_error("Handle USER-ACCR-REQ error");
		}
		break;
	case WMM_CODE_USRLEFT_REP :
		if (wmmUserLeftRep(&msg)) {
			xyz_log_error("Handle USER-LEFT-REP error");
		}
		break;
	case WMM_CODE_USRDROP_REQ :
		if (wmmUserDropReq(&msg)) {
			xyz_log_error("Handle USER-DROP-REQ error");
		}
		break;
	case WMM_CODE_USRACCT_REP :
		if (wmmUserAcctRep(&msg)) {
			xyz_log_error("Handle USER-ACCT-REP error");
		}
		break;
	default:
		xyz_log_error("Unknown WMP message code=%d, length=%d", msg.code, msg.totlen);
		break;
	}
	return 0;
}

static int wmmFirmUpgrReq(WMMSG *msg)
{
	int rc = 0;
	BUFF attr;
	struct img_ident img;

	memset(&attr, 0, sizeof(attr));
	if (wmmGetAttr(msg, &attr) != 1) {
		return -1;
	}
	if (attr.type != WMM_ATTR_IMGID) {
		return -1;
	}
	wmmParseImageIdent(&attr, &img);
	nsap_firm_set_sessid(img.sessid, 16);
	return nsap_firm_upgr_thread();
}

/*
 *
 */
static int wmmNewUserRep(WMMSG *msg)
{
	BUFF attr = BUFF_INIT;

	if (wmmGetAttr(msg, &attr) != 1) {
		xyz_log_error("Get ATTR error");
		return -1;
	}
	if (attr.type != WMM_ATTR_USERINFO) {
		xyz_log_error("New-User-REP unknown attribute type=%d, length=%d", attr.type, attr.len);
		return -1;
	}
	attr.type = NLMSG_ATTR_USERINFO;
	attr.offset = attr.len;
	if (wtpnlmsgHandleNewUserRep(&attr)) {
		xyz_log_error("Handle New-User-REP error");
		return -1;
	}
	if (msg->offset != msg->totlen) {
		xyz_log_error("New-User-REP message exist unknown attribute length %d", (msg->totlen - msg->offset));
	}
	return 0;
}

/*
 *
 */
static int wmmUserAccrReq(WMMSG *msg)
{
	BUFF attr = BUFF_INIT;

	if (wmmGetAttr(msg, &attr) != 1) {
		xyz_log_error("Get ATTR error");
		return -1;
	}
	if (attr.type != WMM_ATTR_USERQUOTA) {
		xyz_log_error("User-ACCR-REQ unknown attribute type=%d, length=%d", attr.type, attr.len);
		return -1;
	}
	attr.type = NLMSG_ATTR_USERQUOTA;
	attr.offset = attr.len;
	if (wtpnlmsgHandleUserAccrReq(&attr)) {
		xyz_log_error("Handle User-ACCR-REQ error");
		return -1;
	}
	if (msg->offset != msg->totlen) {
		xyz_log_error("User-ACCR-REQ message exist unknown attribute length %d", (msg->totlen - msg->offset));
	}
	return 0;
}

/*
 *
 */
static int wmmUserLeftRep(WMMSG *msg)
{
	int rc = 0;
	BUFF attr = BUFF_INIT;
	struct user_info user;

	for ( ; ; ) {
		rc = wmmGetAttr(msg, &attr);
		if (1 == rc) {
			if (attr.type != WMM_ATTR_USERINFO) {
				xyz_log_error("User-Left-REP unknown attribute type=%d, length=%d", attr.type, attr.len);
				continue;
			}
			memset(&user, 0, sizeof(user));
			buffGetU32(&attr, &(user.sn));
			buffGetIPAddr(&attr, &(user.ip));
			buffGetByte(&attr, user.mac, 6);
			buffGetU16(&attr, &(user.wlanindex));
			buffGetU32(&attr, &(user.errcode));
			if (user.errcode) {
				xyz_log_warn("User %02X:%02X:%02X:%02X:%02X:%02X left failed",
						user.mac[0], user.mac[1], user.mac[2],
						user.mac[3], user.mac[4], user.mac[5]);
			}
		} else {
			if (0 == rc) {
				break;
			} else {
				xyz_log_error("Get attribute error");
			}
		}
	}
	if (msg->offset != msg->totlen) {
		xyz_log_error("User-Left-REP message exist unknown attribute length %d", (msg->totlen - msg->offset));
	}

	return rc;
}

/*
 *
 */
static int wmmUserDropReq(WMMSG *msg)
{
	BUFF attr = BUFF_INIT;

	if (wmmGetAttr(msg, &attr) != 1) {
		xyz_log_error("Get ATTR error");
		return -1;
	}
	if (attr.type != WMM_ATTR_USERINFO) {
		xyz_log_error("User-Drop-REQ unknown attribute type=%d, length=%d", attr.type, attr.len);
		return -1;
	}
	attr.type = NLMSG_ATTR_USERINFO;
	attr.offset = attr.len;
	if (wtpnlmsgHandleUserDropReq(&attr)) {
		xyz_log_error("Handle User-Drop-REQ error");
		return -1;
	}
	if (msg->offset != msg->totlen) {
		xyz_log_error("User-Drop-REQ message exist unknown attribute length %d", (msg->totlen - msg->offset));
	}
	return 0;
}

/*
 *
 */
static int wmmUserAcctRep(WMMSG *msg)
{
	if (msg->offset != msg->totlen) {
		xyz_log_error("User-ACCT-REP message exist unknown attribute length %d", (msg->totlen - msg->offset));
	}
	return 0;
}

/*
 *
 */
static int wmmEchoRep(WMMSG *msg)
{
	S_echonum = 0;
	if (msg->offset != msg->totlen) {
		xyz_log_error("ECHO-REP message exist unknown attribute length %d", (msg->totlen - msg->offset));
	}
	xyz_log_debug("Hand ECHO-REP message");
	return 0;
}

static int wmmEchoReq(void)
{
	WMMSG reqmsg;
	uint16_t seqnum = 0;
	uint8_t *apid = NULL;

//	apid = (uint8_t *)nsap_deid_get_byte();
        apid = (uint8_t *)devid;
        
	seqnum = wtpInfoGetSEQNUM();
	if (wmmClearInit(&reqmsg, apid, seqnum, WMM_CODE_ECHO_REQ)) {
		xyz_log_error("Message head INIT error");
		return -1;
	}
	if (udpSend(G_wmpsock, reqmsg.buff, reqmsg.offset, &G_wmpaddr) < 1) {
		xyz_log_error("sendto : %s", strerror(errno));
		return -1;
	}
	xyz_log_debug("Send ECHO-REQ message");
	return 0;
}

static int wmmRebootReq(WMMSG *msg)
{
	WMMSG reqmsg;
	uint16_t seqnum = 0;
	uint8_t *apid = NULL;

	if (msg->offset != msg->totlen) {
		xyz_log_error("REBOOT-REQ message exist unknown attribute length %d", (msg->totlen - msg->offset));
	}

//	apid = (uint8_t *) nsap_deid_get_byte();
        apid = (uint8_t *)devid;
    
	seqnum = wtpInfoGetSEQNUM();
	if (wmmClearInit(&reqmsg, apid, seqnum, WMM_CODE_REBOOT_REP)) {
		xyz_log_error("Message head INIT error");
		return -1;
	}
	if (udpSend(G_wmpsock, reqmsg.buff, reqmsg.offset, &G_wmpaddr) < 1) {
		xyz_log_error("sendto : %s", strerror(errno));
		return -1;
	}
	xyz_log_debug("Send REBOOT-REP message");
	return 0;
}
